package user

import (
	"fmt"
	"math/rand"
	"reflect"
	"sanguo/cluster"
	codecs "sanguo/codec/cs"
	"sanguo/common"
	"sanguo/node/common/asynwrap"
	"sanguo/node/common/db"
	"sanguo/node/common/module"
	"sanguo/node/common/transaction"
	"sanguo/node/node_game"
	cs_message "sanguo/protocol/cs/message"
	ss "sanguo/protocol/ss/message"
	"sync/atomic"
	"time"

	"github.com/go-redis/redis"
	"github.com/golang/protobuf/proto"
	"github.com/sniperHW/kendynet"
)

const (
	status_login1    = 1 //正在登录
	status_login2    = 2 //正在登录
	status_register  = 3 //正在注册
	status_playing   = 4 //正常游戏中
	status_reconnect = 5 //连接已经中断，等待重连
	status_kick      = 6 //正在被踢出
	status_logout    = 7 //正在登出
	status_end       = 8 //已经终止
)

var userMap map[string]*User

type UserData struct {
	Name  string
	Level int32
}

type User struct {
	session            kendynet.StreamSession
	status             uint32
	userData           UserData //用户基础数据
	userID             string
	lastTimer          *cluster.Timer
	transMgr           *transaction.TransactionMgr
	modules            map[string]module.Module
	enterAreaID        int32
	nextSave           time.Time
	nextCheckHeartBeat int64
}

func New(session kendynet.StreamSession, userID string) *User {
	return &User{
		session:  session,
		userID:   userID,
		transMgr: transaction.New(cluster.GetEventQueue()),
		modules:  map[string]module.Module{},
		nextSave: time.Now().Add(time.Second * 60),
	}
}

func GetUser(userID string) *User {
	return userMap[userID]
}

func (this *User) setStatus(flag uint32) {
	atomic.StoreUint32(&this.status, flag)
}

func (this *User) CcheckStatus(flag uint32) bool {
	return this.checkStatus(flag)
}

func (this *User) checkStatus(flag uint32) bool {
	return atomic.LoadUint32(&this.status) == flag
}

func (this *User) GetUserID() string {
	return this.userID
}

func (this *User) GetEnterAreaID() int32 {
	return this.enterAreaID
}

func (this *User) doFinalSave() {
	this.finalSave(func(err error) {
		if nil == err {
			clearUserGame(this.userID, func() {
				this.setStatus(status_end)
				delete(userMap, this.userID)
			})
		} else {
			//try again
			node_game.Errorln(this.userID, "finalSave error:", err, " try again")
			this.doFinalSave()
		}
	})
}

func (this *User) finalTimeout() {
	node_game.Infoln(this.userID, "finalTimeout")
	this.setStatus(status_logout)
	this.lastTimer = nil
	this.doFinalSave()
}

func (this *User) OnDisConnect(reason string) {
	node_game.Infoln(this.userID, "OnDisConnect:", reason)
	this.session = nil
	if this.checkStatus(status_playing) || this.checkStatus(status_kick) {
		//上报最大等级
		this.GetModuleByName("role").(interface {
			ReportMaxLev()
		}).ReportMaxLev()

		this.GetModuleByName("role").(interface {
			Save(func(error))
		}).Save(func(err error) {})

		this.GetModuleByName("role").(interface {
			OffLine()
		}).OffLine()

		//		this.GetModuleByName("role").(interface {
		//			ClearMap()
		//		}).ClearMap()

		//		this.GetModuleByName("role").(interface {
		//			ClearInstanceZone()
		//		}).ClearInstanceZone()

		this.setStatus(status_reconnect)
		/*
		 * 设置定时器，如果玩家在定时器到期前没有重连上来销毁对象
		 */
		this.lastTimer = cluster.RegisterTimer(time.Now().Add(time.Second*600), this.finalTimeout)
	}
}

func (this *User) durationSave(now time.Time) {

	if this.checkStatus(status_playing) {
		if this.nextSave.After(now) {
			r := this.GetModuleByName("role")
			if nil != r {
				r.(interface {
					Save(func(error))
				}).Save(nil)
			}
			this.nextSave = time.Now().Add(time.Second*60 + time.Duration(rand.Int()%10))
		}
	}
}

func (this *User) checkHeartbeat(now time.Time) {
	if this.checkStatus(status_playing) {
		if now.Unix() > this.nextCheckHeartBeat {
			//清空待处理的trans
			this.transMgr.Clear()

			if nil != this.session {
				this.session.Close("Heartbeat Timeout", time.Second)
			}
		}
	}
}

func (this *User) Tick(now time.Time) {
	if !this.checkStatus(status_end) {
		this.checkHeartbeat(now)
		this.durationSave(now)
		this.transMgr.Tick(now)
		for _, v := range this.modules {
			v.Tick(now)
		}
	}
}

func clearUserGame(userID string, callback func()) {
	clearUserStr := `
		local game = redis.call('hget',KEYS[1],ARGV[1])
		if not game then
			return 'ok'
		else
			if game == ARGV[2] then
				redis.call('hdel',KEYS[1],ARGV[1])
			end
			return 'ok'
		end
	`

	val := node_game.GetSelfService().ToPeerID().ToString()

	keys := []string{}
	keys = append(keys, "game_user_map")
	asynwrap.Wrap(db.GetRedisClient("login").Eval)(func(ret []interface{}) {
		_, err := ret[0].(*redis.Cmd).Result()
		if nil != err {
			node_game.Errorln("clearUserGame error:", err.Error())
		}
		node_game.Infoln(userID, "clearUserGame ok")
		if nil != callback {
			callback()
		}
	}, clearUserStr, keys, userID, val)

	node_game.RemoveToken(userID)
}

func onKick(session kendynet.StreamSession, msg proto.Message) {
	kick := msg.(*ss.Kick)
	userID := kick.GetUserID()
	u := userMap[userID]
	if nil != u {
		u.kick()
	} else {
		clearUserGame(userID, nil)
	}
}

func (this *User) Reply(seqNo uint16, msg proto.Message) {
	if nil != this.session {
		this.session.Send(codecs.NewMessage(seqNo, msg))
	}
}

func (this *User) Post(msg proto.Message) {
	if nil != this.session {
		node_game.Debugln(this.GetUserID(), "msg", reflect.TypeOf(msg).String(), msg)
		this.session.Send(codecs.NewMessage(0, msg))
	}
}

func Tick() {
	now := time.Now()
	for _, v := range userMap {
		v.Tick(now)
	}
	cluster.RegisterTimer(time.Now().Add(time.Second), Tick)
}

func onRelay(session kendynet.StreamSession, msg proto.Message) {
	//将接收到的消息直接透传给目标玩家
	relay := msg.(*ss.Relay)
	users := relay.GetUsers()
	data := relay.GetMsg()
	//fmt.Println("F", users, len(data), relay.GetMsgType())
	if len(data) > 0 {
		for _, v := range users {
			u := userMap[v]
			/*截取消息*/
			SessionMsg(data, u)

			if u != nil && u.checkStatus(status_playing) && u.session != nil {
				//fmt.Println("relay", relay.GetMsgType(), "to", u.userID)
				u.session.SendMessage(kendynet.NewByteBuffer(data, len(data)))
			}
		}
	}
}

func onHeartbeat(session kendynet.StreamSession, msg *codecs.Message) {
	if session.GetUserData() == nil {
		node_game.Debugln("onHeartbeat session.GetUserData() == nil")
		return
	}
	u := session.GetUserData().(*User)
	_, ok := userMap[u.userID]
	if ok {
		u.nextCheckHeartBeat = time.Now().Unix() + common.HeartBeat_Timeout_Client
		fmt.Println(u.GetUserID(), "OnHeartBeat")
		Heartbeat := &cs_message.HeartbeatToC{}
		session.Send(codecs.NewMessage(0, Heartbeat))
	}
}

func init() {
	userMap = map[string]*User{}
	transFactory = map[string]TransCreateFunc{}
	moduleFactory = map[string]factoryFunc{}
	node_game.RegisterHandler(&cs_message.GameLoginToS{}, onLogin)
	node_game.RegisterHandler(&cs_message.ReconnectToS{}, onReconnect)
	node_game.RegisterHandler(&cs_message.HeartbeatToS{}, onHeartbeat)
	node_game.SetDefaultHandler(onUserMsg)
	cluster.Register(&ss.Kick{}, onKick)
	cluster.Register(&ss.Relay{}, onRelay)
	cluster.RegisterTimer(time.Now().Add(time.Second), Tick)
}
